/*********************
 Author: Ysrael Salire
 Title: BoxPlex
 Type: Game
 Version: 1.1.0
 Date: 8/15/11
 Resources used:
 -BACK TO 1982 (1001 Free Fonts)
 -Allegro 5.0.3
 -Sounds from Oplex Origins
 ********************/

#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <allegro5/allegro.h>
#include <allegro5/allegro_audio.h>
#include <allegro5/allegro_acodec.h>
#include <allegro5/allegro_font.h>
#include <allegro5/allegro_image.h>
#include <allegro5/allegro_native_dialog.h>
#include <allegro5/allegro_primitives.h>
#include <allegro5/allegro_ttf.h>
#include <fstream>
#include <iostream>
#include <sstream>

#define NONE 0
#define UP 1
#define DOWN 2
#define LEFT 3
#define RIGHT 4

float FPS = 60;
float BOUNCE_SPEED = 4.0;
int SCREEN_W = 640;
int SCREEN_H = 480;
int BOUNCER_SIZE = 20;
int GRID_W = 30;
int GRID_H = 22;
int FONT_SIZE = 18;
int MAX_LEVEL = 50;
using namespace std;

class BOXES {
	float x, y, dx, dy;
	ALLEGRO_BITMAP *bouncer;
public:
	float getX()
	{
		return x;
	}
	float getY()
	{
		return y;
	}
	float getDx()
	{
		return dx;
	}
	float getDy()
	{
		return dy;
	}
	void setValues(float inX, float inY, float inDx, float inDy)
	{
		x = inX;
		y = inY;
		dx = inDx;
		dy = inDy;
	}
	int initBmp()
	{
		bouncer = al_load_bitmap("resources/swirls.bmp");//create_bitmap(BOUNCER_SIZE, BOUNCER_SIZE);
		if(!bouncer)
		  return 1;
		/*al_set_target_bitmap(bouncer);
		al_clear_to_color(al_map_rgb(122,122,122));*/
		return 0;
	}
	void destBmp()
	{
		al_destroy_bitmap(bouncer);
	}
	void drawBmp()
	{
		al_draw_bitmap(bouncer, x, y, 0);
	}
};

class BLOCKS {
	bool enabled, pend_ul, pend_dr, checked;
	int direction;
	float x, y;
	ALLEGRO_BITMAP *block, *pending_ul_bmp, *pending_dr_bmp, *blah;
public:
	int initBmp()
	{
		block = al_load_bitmap("resources/block.bmp");
		if(!block)
		  return 1;
		pending_ul_bmp = al_create_bitmap(BOUNCER_SIZE,BOUNCER_SIZE);
		al_set_target_bitmap(pending_ul_bmp);
		al_clear_to_color(al_map_rgb(255,0,0));
		pending_dr_bmp = al_create_bitmap(BOUNCER_SIZE,BOUNCER_SIZE);
		al_set_target_bitmap(pending_dr_bmp);
		al_clear_to_color(al_map_rgb(0,0,255));
		blah = al_create_bitmap(BOUNCER_SIZE,BOUNCER_SIZE);
		al_set_target_bitmap(blah);
		al_clear_to_color(al_map_rgb(0,255,0));
		enabled = true;
		direction = 0;
		pend_ul = false;
		pend_dr = false;
		checked = false;
		return 0;
	}
	void destBmp()
	{
		al_destroy_bitmap(block);
	}
	void drawBmp()
	{
		if(checked)
			al_draw_bitmap(block, x*BOUNCER_SIZE, y*BOUNCER_SIZE, 0);
		else
			al_draw_bitmap(blah, x*BOUNCER_SIZE, y*BOUNCER_SIZE, 0);
	}
	void drawPend()
	{
		if(direction == 1 || direction == 3)
			al_draw_bitmap(pending_ul_bmp, x*BOUNCER_SIZE, y*BOUNCER_SIZE, 0);
		if(direction == 2 || direction == 4)
			al_draw_bitmap(pending_dr_bmp, x*BOUNCER_SIZE, y*BOUNCER_SIZE, 0);
	}
	void setValues(float inX, float inY)
	{
		x = inX;
		y = inY;
	}
	void disable()
	{
		enabled = false;
		checked = true;
	}
	void enable()
	{
		enabled = true;
	}
	bool able()
	{
		return enabled;
	}
	int dir()
	{
		return direction;
	}
	float getX()
	{
		return x;
	}
	float getY()
	{
		return y;
	}
	void go(int way)
	{
		direction = way; // 1 is up, 2 is down, 0 is done, 3 is left, 4 is right
		if(direction == 1 || direction == 3)
			pend_ul = true;
		else if(direction == 2 || direction == 4)
			pend_dr = true;
	}
	bool pending_ul()
	{
		return pend_ul;
	}
	bool pending_dr()
	{
		return pend_dr;
	}
	bool isChecked()
	{
		return checked;
	}
	void putcheck()
	{
		checked = true;
	}
	void uncheck()
	{
		checked = false;
	}
};

//necessary globals
BOXES *boxes = NULL;
BLOCKS **blocks = NULL;

void check(int x, int y)
{
	// a recursive function to mark the regions with boxes
	blocks[x][y].putcheck();
	if(x + 1 < GRID_W + 1 && blocks[x + 1][y].able() && !blocks[x + 1][y].isChecked())
		check(x + 1, y);
	if(x - 1 > 0 && blocks[x - 1][y].able() && !blocks[x - 1][y].isChecked())
		check(x - 1, y);
	if(y - 1 > 0 && blocks[x][y - 1].able() && !blocks[x][y - 1].isChecked())
		check(x, y - 1);
	if(y + 1 < GRID_H + 1 && blocks[x][y + 1].able() && !blocks[x][y + 1].isChecked())
		check(x, y + 1);
}

int main(int argc, char **argv)
{
    /********************
    * fancy table       *
    * 1 FPS             *
    * 2 BOUNCE_SPEED    *
    * 3 SCREEN_W        *
    * 4 SCREEN_H        *
    * 5 BOUNCER_SIZE    *
    * 6 GRID_W          *
    * 7 GRID_H          *
    * 8 FONT_SIZE       *
    * 9 MAX_LEVEL       *
    * 10 HIGH_SCORE     *
    ********************/
    fstream config;
    ostringstream os;
    int var, HIGH_SCORE = 0;
    float val;
    string str;
    config.open("config.cfg", fstream::in | fstream::out);
    if(config.is_open()) // if the file is not found it will fall back to default values
        while(getline(config,str))
        {
            stringstream ss(str);
            if(ss >> var >> val)
            {
                switch(var)
                {
                    case 1:
                        FPS = val;
                        break;
                    case 2:
                        BOUNCE_SPEED = val;
                        break;
                    case 3:
                        SCREEN_W = val;
                        break;
                    case 4:
                        SCREEN_H = val;
                        break;
                    case 5:
                        BOUNCER_SIZE = val;
                        break;
                    case 6:
                        GRID_W = val;
                        break;
                    case 7:
                        GRID_H = val;
                        break;
                    case 8:
                        FONT_SIZE = val;
                        break;
                    case 9:
                        MAX_LEVEL = val;
                        break;
                    case 10:
                        HIGH_SCORE = val;
                        break;
                }
            }
        }
	int font_r, font_g, font_b, rotated = 0, grid_count = GRID_W * GRID_H, taken = 0, line_test = 0, levels = 1, stop_dir = NONE, lives, initial_boxes = 1,
	 score = 0, decay = 0, pos = 0;
	bool mouse_focus = false, line_started = false, redraw = true, doexit = false, level_done = false, destroying_ul = false, destroying_dr = false,
		check_ul = false, check_dr = false, level_start = false;
	font_r = font_g = font_b = 255;
	float box_x = 0.0, box_y = 0.0, mouse_x = SCREEN_W / 2.0 - BOUNCER_SIZE / 2.0, mouse_y = SCREEN_H / 2.0 - BOUNCER_SIZE / 2.0;
	lives = levels;
	srand(time(NULL));
	//start init
	ALLEGRO_DISPLAY *display = NULL;
	ALLEGRO_EVENT_QUEUE *event_queue = NULL;
	ALLEGRO_FONT *font = NULL, *banner_font = NULL;
	ALLEGRO_TIMER *timer = NULL;
	ALLEGRO_BITMAP *mouse_bmp = NULL;
	ALLEGRO_SAMPLE *song = NULL, *attack = NULL, *start = NULL, *quit = NULL;
	//ALLEGRO_SAMPLE_ID *song_id = NULL;
	boxes = new BOXES[MAX_LEVEL];
	blocks = new BLOCKS*[GRID_W + 2]; // +2 for column of checks on each side

   if(!al_init()) {
      fprintf(stderr, "failed to initialize allegro!\n");
      return -1;
   }

   if(!al_install_audio())
	{
		fprintf(stderr, "failed to initialize audio!\n");
		return -1;
	}

    if(!al_init_acodec_addon())
	{
		fprintf(stderr, "failed to initialize audio codecs!\n");
		return -1;
	}

 	if (!al_reserve_samples(4))
	{
                fprintf(stderr, "failed to reserve samples!\n");
		return -1;
	}

	song = al_load_sample( "resources/song.ogg" );
	if (!song)
	{
		printf( "Audio clip song not loaded!\n" );
		return -1;
	}
	quit = al_load_sample( "resources/quit.ogg" );
	if (!quit)
	{
		printf( "Audio clip quit not loaded!\n" );
		return -1;
	}
	start = al_load_sample( "resources/start.ogg" );
	if (!start)
	{
		printf( "Audio clip start not loaded!\n" );
		return -1;
	}
	attack = al_load_sample( "resources/attack.ogg" );
	if (!attack)
	{
		printf( "Audio clip attack not loaded!\n" );
		return -1;
	}

   if(!al_install_mouse()) {
      fprintf(stderr, "failed to initialize the mouse!\n");
      return -1;
   }

   if(!al_install_keyboard()) {
	  fprintf(stderr, "failed to initialize the keyboard!\n");
	  return -1;
   }

   timer = al_create_timer(1.0 / FPS);
   if(!timer) {
      fprintf(stderr, "failed to create timer!\n");
      return -1;
   }

   display = al_create_display(SCREEN_W, SCREEN_H);
   if(!display) {
      fprintf(stderr, "failed to create display!\n");
      return -1;
   }
   al_set_window_title(display, "BoxPlex");

	al_init_image_addon();
	al_init_font_addon();
	al_init_ttf_addon();
	al_init_primitives_addon();
	font = al_load_ttf_font("resources/Backto1982.ttf",FONT_SIZE,0);
	if(!font)
	{
		fprintf(stderr, "failed to create font!\n");
		return -1;
	}
	banner_font = al_load_ttf_font("resources/Backto1982.ttf",4*FONT_SIZE,0);
	if(!banner_font)
	{
		fprintf(stderr, "failed to create banner_font!\n");
		return -1;
	}

   for(int i = 0; i < MAX_LEVEL; i++)
	if(boxes[i].initBmp())
	{
      fprintf(stderr, "failed to create bouncer bitmap!\n");
	  return -1;
	}

	for(int i = 0; i < GRID_W + 2; i++)
	{
		blocks[i] = new BLOCKS[GRID_H + 2]; // +2 for row of checks on each side EDIT: this is useless now, I should take it out
		for(int j = 0; j < GRID_H + 2; j++)
		{
			blocks[i][j].setValues(i,j);
			if(blocks[i][j].initBmp())
			{
				fprintf(stderr, "failed to create block bitmap!\n");
				return -1;
			}
		}
	}

	mouse_bmp = al_load_bitmap("resources/cursor.bmp");
	if(!mouse_bmp)
	{
		fprintf(stderr, "failed to create mouse bitmap!\n");
		return -1;
	}

	al_set_display_icon(display, mouse_bmp);
   // end init

   al_set_target_bitmap(al_get_backbuffer(display));

   event_queue = al_create_event_queue();
   if(!event_queue) {
      fprintf(stderr, "failed to create event_queue!\n");
      return -1;
   }

   al_register_event_source(event_queue, al_get_display_event_source(display));
   al_register_event_source(event_queue, al_get_timer_event_source(timer));
   al_register_event_source(event_queue, al_get_keyboard_event_source());
   al_register_event_source(event_queue, al_get_mouse_event_source());

   al_clear_to_color(al_map_rgb(0,0,0));

   al_flip_display();

   al_start_timer(timer);

    initial_boxes = rand() % 49 + 1;

    for(int i = 0; i < initial_boxes; i++) // randomize boxes
    {
       box_x = rand() % SCREEN_W;
       box_x = (box_x > SCREEN_W / 2.0) ? (box_x -= (BOUNCER_SIZE + BOUNCER_SIZE + 5)) : (box_x += BOUNCER_SIZE + BOUNCER_SIZE + 5);
       box_y = rand() % SCREEN_H;
       box_y = (box_y > SCREEN_H / 2.0) ? (box_y -= BOUNCER_SIZE + BOUNCER_SIZE + 5) : (box_y += BOUNCER_SIZE + BOUNCER_SIZE + 5);
       boxes[i].setValues(box_x, box_y, (1 - 2 * (rand() % 2)) * BOUNCE_SPEED, (1 - 2 * (rand() % 2)) * BOUNCE_SPEED);
    }


   al_hide_mouse_cursor(display);
	al_play_sample(song, 1.0, 0.0, 1.0, ALLEGRO_PLAYMODE_ONCE, NULL);

   while(!level_start && !doexit) // here is the menu
   {
       ALLEGRO_EVENT ev;
      al_wait_for_event(event_queue, &ev);

      if(ev.type == ALLEGRO_EVENT_KEY_DOWN)
          if(ev.keyboard.keycode == ALLEGRO_KEY_ESCAPE)
          {
              doexit = true;
              level_start = true;
          }
        if(ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE)
      {
          level_start = true;
          doexit = true;
      }

      if(ev.type == ALLEGRO_EVENT_MOUSE_AXES || ev.type == ALLEGRO_EVENT_MOUSE_ENTER_DISPLAY)
      {
          mouse_x = ev.mouse.x;
          mouse_y = ev.mouse.y;
          mouse_focus = true;
      }

      if(ev.type == ALLEGRO_EVENT_MOUSE_LEAVE_DISPLAY)
          mouse_focus = false;

      if(ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN)
      {
          if(ev.mouse.button == 1)
          {
              if(mouse_x > 219 && mouse_x < 421 && mouse_y > 240 && mouse_y < 260)
              {
                level_start = true;
                al_play_sample(start, 1.0, 0.0, 1.0, ALLEGRO_PLAYMODE_ONCE, NULL);
              }
              else if(mouse_x > 180 && mouse_x < 460 && mouse_y > 275 && mouse_y < 300)
              {
                  doexit = true;
                  al_play_sample(quit, 1.0, 0.0, 1.0, ALLEGRO_PLAYMODE_ONCE, NULL);
              }
          }
          else if(ev.mouse.button == 2)
              rotated = 1 - rotated;
      }
          /*else if(ev.mouse.button == 3)
              cout << "(" << mouse_x << ", " << mouse_y << ")\n";*/


      if(ev.type == ALLEGRO_EVENT_TIMER) { // frame

          for(int i = 0; i < initial_boxes; i++)
          {
             if(boxes[i].getX() + boxes[i].getDx() < BOUNCER_SIZE || boxes[i].getX() + boxes[i].getDx() > SCREEN_W - BOUNCER_SIZE - BOUNCER_SIZE) // hits borders lr
                 boxes[i].setValues(boxes[i].getX(), boxes[i].getY(), -boxes[i].getDx(), boxes[i].getDy());
             else if(boxes[i].getY() + boxes[i].getDy() < BOUNCER_SIZE || boxes[i].getY() + boxes[i].getDy() > SCREEN_H - BOUNCER_SIZE - BOUNCER_SIZE) // hits borders ud
                 boxes[i].setValues(boxes[i].getX(), boxes[i].getY(), boxes[i].getDx(), -boxes[i].getDy());

             boxes[i].setValues(boxes[i].getX() + boxes[i].getDx(), boxes[i].getY() + boxes[i].getDy(), boxes[i].getDx(), boxes[i].getDy());
          }
         redraw = true;
      } // if frame
      if(redraw && al_is_event_queue_empty(event_queue)) {
         redraw = false;

         al_clear_to_color(al_map_rgb(0,0,0));
         al_draw_text(banner_font, al_map_rgb(font_r,font_g,font_b), SCREEN_W / 2.0, 0, ALLEGRO_ALIGN_CENTRE, "BoxPlex");
         if(mouse_x > 219 && mouse_x < 421 && mouse_y > 240 && mouse_y < 260)
            al_draw_text(font, al_map_rgb(0,255,0), SCREEN_W / 2.0, SCREEN_H / 2.0, ALLEGRO_ALIGN_CENTRE, "Save humanity");
         else
            al_draw_text(font, al_map_rgb(255,255,255), SCREEN_W / 2.0, SCREEN_H / 2.0, ALLEGRO_ALIGN_CENTRE, "Save humanity");
         if(mouse_x > 180 && mouse_x < 460 && mouse_y > 275 && mouse_y < 300)
            al_draw_text(font, al_map_rgb(0,255,0), SCREEN_W / 2.0, SCREEN_H / 2.0 + 2*FONT_SIZE, ALLEGRO_ALIGN_CENTRE, "Flee into the night");
         else
            al_draw_text(font, al_map_rgb(255,255,255), SCREEN_W / 2.0, SCREEN_H / 2.0 + 2*FONT_SIZE, ALLEGRO_ALIGN_CENTRE, "Flee into the night");


        font_r = font_r % 255 + 20;
        font_g = font_g % 255 + 40;
        font_b = font_b % 255 + 60;
         for(int i = 0; i < initial_boxes; i++)
             boxes[i].drawBmp();

         if(mouse_focus && !level_done)
            al_draw_rotated_bitmap(mouse_bmp, al_get_bitmap_width(mouse_bmp)/2, al_get_bitmap_height(mouse_bmp)/2, mouse_x, mouse_y, ALLEGRO_PI / 2 * rotated, 0);

         al_flip_display();
      } // if redraw
   } // end menu

   //al_stop_sample(song_id);
   level_start = false;

   while(!doexit) // here is the game loop
   {
	   for(int i = 0; i < levels; i++) // randomize box locations
		{
		   box_x = rand() % SCREEN_W;
		   box_x = (box_x > SCREEN_W / 2.0) ? (box_x -= (BOUNCER_SIZE + BOUNCER_SIZE + 5)) : (box_x += BOUNCER_SIZE + BOUNCER_SIZE + 5);
		   box_y = rand() % SCREEN_H;
		   box_y = (box_y > SCREEN_H / 2.0) ? (box_y -= BOUNCER_SIZE + BOUNCER_SIZE + 5) : (box_y += BOUNCER_SIZE + BOUNCER_SIZE + 5);
		   boxes[i].setValues(box_x, box_y, (1 - 2 * (rand() % 2)) * BOUNCE_SPEED, (1 - 2 * (rand() % 2)) * BOUNCE_SPEED);
		}
	   while(!level_done && lives > 0)
	   {
		  ALLEGRO_EVENT ev;
		  al_wait_for_event(event_queue, &ev);

		  if(ev.type == ALLEGRO_EVENT_KEY_DOWN)
			  if(ev.keyboard.keycode == ALLEGRO_KEY_ESCAPE)
			  {
				  doexit = true;
				  level_done = true;
			  }
			  /*else if(ev.keyboard.keycode == ALLEGRO_KEY_R)
				  level_done = true;
			  else if(ev.keyboard.keycode == ALLEGRO_KEY_S)
			  {
				  level_done = true;
				  levels--;
			  }*/


		  if(ev.type == ALLEGRO_EVENT_TIMER) { // frame
		      decay = (decay + 1) % 90; // score goes down as player dallies
			  line_test = (line_test + 1) % 4;
			  if(!decay && score > 0)
                score--;
			  for(int k = 1; k < GRID_W + 1; k++)
					 for(int m = 1; m < GRID_H + 1; m++)
						 if(blocks[k][m].able())
							 blocks[k][m].uncheck();
			  for(int i = 0; i < levels; i++)
			  {
				  if(boxes[i].getX() > 1.5*BOUNCER_SIZE && boxes[i].getX() < SCREEN_W - BOUNCER_SIZE - 1.5*BOUNCER_SIZE)
				  {
					  if(boxes[i].getDx() > 0 && !blocks[(int)(boxes[i].getX() + boxes[i].getDx())/BOUNCER_SIZE + 1][(int)(boxes[i].getY())/BOUNCER_SIZE].able()) // hits left side
					  {
						  if(!blocks[(int)(boxes[i].getX() + boxes[i].getDx())/BOUNCER_SIZE + 1][(int)(boxes[i].getY())/BOUNCER_SIZE + 1].able() || !blocks[(int)(boxes[i].getX() + boxes[i].getDx())/BOUNCER_SIZE + 1][(int)(boxes[i].getY())/BOUNCER_SIZE - 1].able())
							  boxes[i].setValues(boxes[i].getX(), boxes[i].getY(), -boxes[i].getDx(), boxes[i].getDy());
					  }
					  else if(boxes[i].getDx() < 0 && !blocks[(int)(boxes[i].getX() + boxes[i].getDx())/BOUNCER_SIZE][(int)(boxes[i].getY())/BOUNCER_SIZE].able()) // hits right side
						  if(!blocks[(int)(boxes[i].getX() + boxes[i].getDx())/BOUNCER_SIZE][(int)(boxes[i].getY())/BOUNCER_SIZE + 1].able() || !blocks[(int)(boxes[i].getX() + boxes[i].getDx())/BOUNCER_SIZE][(int)(boxes[i].getY())/BOUNCER_SIZE - 1].able())
							boxes[i].setValues(boxes[i].getX(), boxes[i].getY(), -boxes[i].getDx(), boxes[i].getDy());
					  if(blocks[(int)(boxes[i].getX() + boxes[i].getDx())/BOUNCER_SIZE + 1][(int)(boxes[i].getY())/BOUNCER_SIZE].dir()) // hits drawing line left
					  {
						  stop_dir = blocks[(int)(boxes[i].getX() + boxes[i].getDx())/BOUNCER_SIZE + 1][(int)(boxes[i].getY())/BOUNCER_SIZE].dir();
						  if(stop_dir == UP)
							  destroying_ul = false;
						  else if(stop_dir == DOWN)
							  destroying_dr = false;
						  for(int k = 0; k < GRID_W + 2; k++)
							for(int j = 0; j < GRID_H + 2; j++)
								if(blocks[k][j].dir() == stop_dir)
									blocks[k][j].go(NONE);
						  lives--;
						  al_play_sample(attack, 1.0, 0.0, 1.0, ALLEGRO_PLAYMODE_ONCE, NULL);
					  }
					  else if(blocks[(int)(boxes[i].getX() + boxes[i].getDx())/BOUNCER_SIZE][(int)(boxes[i].getY())/BOUNCER_SIZE].dir()) // hits drawing line right
					  {
						  stop_dir = blocks[(int)(boxes[i].getX() + boxes[i].getDx())/BOUNCER_SIZE][(int)(boxes[i].getY())/BOUNCER_SIZE].dir();
						  if(stop_dir == UP)
							  destroying_ul = false;
						  else if(stop_dir == DOWN)
							  destroying_dr = false;
						  for(int k = 0; k < GRID_W + 2; k++)
							for(int j = 0; j < GRID_H + 2; j++)
								if(blocks[k][j].dir() == stop_dir)
									blocks[k][j].go(NONE);
						  lives--;
						  al_play_sample(attack, 1.0, 0.0, 1.0, ALLEGRO_PLAYMODE_ONCE, NULL);
					  }
				  }
				 else if(boxes[i].getX() + boxes[i].getDx() < BOUNCER_SIZE || boxes[i].getX() + boxes[i].getDx() > SCREEN_W - BOUNCER_SIZE - BOUNCER_SIZE) // hits borders lr
					 boxes[i].setValues(boxes[i].getX(), boxes[i].getY(), -boxes[i].getDx(), boxes[i].getDy());

				 if(boxes[i].getY() > 1.5*BOUNCER_SIZE && boxes[i].getY() < SCREEN_H - BOUNCER_SIZE - 1.5*BOUNCER_SIZE)
				  {
					  if(boxes[i].getDy() > 0 && !blocks[(int)(boxes[i].getX())/BOUNCER_SIZE][(int)(boxes[i].getY() + boxes[i].getDy())/BOUNCER_SIZE + 1].able()) // hits top side
					  {
						  if(!blocks[(int)(boxes[i].getX())/BOUNCER_SIZE + 1][(int)(boxes[i].getY() + boxes[i].getDy())/BOUNCER_SIZE + 1].able() || !blocks[(int)(boxes[i].getX())/BOUNCER_SIZE - 1][(int)(boxes[i].getY() + boxes[i].getDy())/BOUNCER_SIZE + 1].able())
							  boxes[i].setValues(boxes[i].getX(), boxes[i].getY(), boxes[i].getDx(), -boxes[i].getDy());
					  }
					  else if(boxes[i].getDy() < 0 && !blocks[(int)(boxes[i].getX())/BOUNCER_SIZE][(int)(boxes[i].getY() + boxes[i].getDy())/BOUNCER_SIZE].able()) // hits bottom side
						  if(!blocks[(int)(boxes[i].getX())/BOUNCER_SIZE + 1][(int)(boxes[i].getY() + boxes[i].getDy())/BOUNCER_SIZE].able() || !blocks[(int)(boxes[i].getX())/BOUNCER_SIZE - 1][(int)(boxes[i].getY() + boxes[i].getDy())/BOUNCER_SIZE].able())
							  boxes[i].setValues(boxes[i].getX(), boxes[i].getY(), boxes[i].getDx(), -boxes[i].getDy());
					  if(blocks[(int)(boxes[i].getX())/BOUNCER_SIZE + 1][(int)(boxes[i].getY() + boxes[i].getDy())/BOUNCER_SIZE + 1].dir()) // hits drawing line top
					  {
						  stop_dir = blocks[(int)(boxes[i].getX())/BOUNCER_SIZE + 1][(int)(boxes[i].getY() + boxes[i].getDy())/BOUNCER_SIZE + 1].dir();
						  if(stop_dir == LEFT)
							  destroying_ul = false;
						  else if(stop_dir == RIGHT)
							  destroying_dr = false;
						  for(int k = 0; k < GRID_W + 2; k++)
							for(int j = 0; j < GRID_H + 2; j++)
								if(blocks[k][j].dir() == stop_dir)
									blocks[k][j].go(NONE);
						  lives--;
						  al_play_sample(attack, 1.0, 0.0, 1.0, ALLEGRO_PLAYMODE_ONCE, NULL);
					  }
					  else if(blocks[(int)(boxes[i].getX())/BOUNCER_SIZE + 1][(int)(boxes[i].getY() + boxes[i].getDy())/BOUNCER_SIZE].dir()) // hits drawing line bottom
					  {
						  stop_dir = blocks[(int)(boxes[i].getX())/BOUNCER_SIZE + 1][(int)(boxes[i].getY() + boxes[i].getDy())/BOUNCER_SIZE].dir();
						  if(stop_dir == LEFT)
							  destroying_ul = false;
						  else if(stop_dir == RIGHT)
							  destroying_dr = false;
						  for(int k = 0; k < GRID_W + 2; k++)
							for(int j = 0; j < GRID_H + 2; j++)
								if(blocks[k][j].dir() == stop_dir)
									blocks[k][j].go(NONE);
						  lives--;
						  al_play_sample(attack, 1.0, 0.0, 1.0, ALLEGRO_PLAYMODE_ONCE, NULL);
					  }
				  }
				 else if(boxes[i].getY() + boxes[i].getDy() < BOUNCER_SIZE || boxes[i].getY() + boxes[i].getDy() > SCREEN_H - BOUNCER_SIZE - BOUNCER_SIZE) // hits borders ud
					 boxes[i].setValues(boxes[i].getX(), boxes[i].getY(), boxes[i].getDx(), -boxes[i].getDy());
				 check_ul = false;
				 check_dr = false;
				 for(int k = 0; k < GRID_W + 2; k++) // this is inefficient but it fixes the misinterpreted detection of drawing lines
					for(int j = 0; j < GRID_H + 2; j++)
						if(blocks[k][j].dir() == LEFT || blocks[k][j].dir() == UP)
						{
							check_ul = true;
							break;
						}
				 for(int k = 0; k < GRID_W + 2; k++)
					for(int j = 0; j < GRID_H + 2; j++)
						if(blocks[k][j].dir() == RIGHT || blocks[k][j].dir() == DOWN)
						{
							check_dr = true;
							break;
						}
				destroying_ul = check_ul;
				destroying_dr = check_dr;

				 boxes[i].setValues(boxes[i].getX() + boxes[i].getDx(), boxes[i].getY() + boxes[i].getDy(), boxes[i].getDx(), boxes[i].getDy());

				check((int)(boxes[i].getX())/BOUNCER_SIZE, (int)(boxes[i].getY())/BOUNCER_SIZE);
			  }

			  for(int k = 1; k < GRID_W + 1; k++)
					for(int m = 1; m < GRID_H + 1; m++)
						if(!blocks[k][m].isChecked())
							blocks[k][m].disable();
			 redraw = true;
		  }

		  if(ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE)
		  {
			  level_done = true;
			  doexit = true;
		  }

		  if(ev.type == ALLEGRO_EVENT_MOUSE_AXES || ev.type == ALLEGRO_EVENT_MOUSE_ENTER_DISPLAY)
		  {
			  mouse_x = ev.mouse.x;
			  mouse_y = ev.mouse.y;
			  mouse_focus = true;
		  }

		  if(ev.type == ALLEGRO_EVENT_MOUSE_LEAVE_DISPLAY)
			  mouse_focus = false;

		  if(ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN)
		  {
			  if(ev.mouse.button == 1)
			  {
				  if(ev.mouse.x > BOUNCER_SIZE && ev.mouse.y > BOUNCER_SIZE && ev.mouse.x < (SCREEN_W - BOUNCER_SIZE) && ev.mouse.y < (SCREEN_H - BOUNCER_SIZE) && blocks[(int)(ev.mouse.x)/BOUNCER_SIZE][(int)(ev.mouse.y)/BOUNCER_SIZE].able())
				  {
					  if(!destroying_ul)
					  {
						  destroying_ul = true;
						  blocks[(int)(ev.mouse.x)/BOUNCER_SIZE][(int)(ev.mouse.y)/BOUNCER_SIZE].go(LEFT);
						  if(!rotated)
							  blocks[(int)(ev.mouse.x)/BOUNCER_SIZE][(int)(ev.mouse.y)/BOUNCER_SIZE].go(UP);
						  //blocks[(int)(ev.mouse.x)/BOUNCER_SIZE][(int)(ev.mouse.y)/BOUNCER_SIZE].disable();
					  }
					  if((int)(ev.mouse.x)/BOUNCER_SIZE < GRID_W && rotated && !destroying_dr && blocks[(int)(ev.mouse.x)/BOUNCER_SIZE + 1][(int)(ev.mouse.y)/BOUNCER_SIZE].able())

					  {
						  destroying_dr = true;
						  blocks[(int)((ev.mouse.x)/BOUNCER_SIZE + rotated)][(int)((ev.mouse.y)/BOUNCER_SIZE + 1 - rotated)].go(RIGHT);
						  //blocks[(int)((ev.mouse.x)/BOUNCER_SIZE + rotated)][(int)((ev.mouse.y)/BOUNCER_SIZE + 1 - rotated)].disable();
					  }
					  if((int)(ev.mouse.y)/BOUNCER_SIZE < GRID_H && !rotated && !destroying_dr && blocks[(int)(ev.mouse.x)/BOUNCER_SIZE][(int)(ev.mouse.y)/BOUNCER_SIZE + 1].able())
					  {
						  destroying_dr = true;
						  blocks[(int)((ev.mouse.x)/BOUNCER_SIZE + rotated)][(int)((ev.mouse.y)/BOUNCER_SIZE + 1 - rotated)].go(DOWN);
						  //blocks[(int)((ev.mouse.x)/BOUNCER_SIZE + rotated)][(int)((ev.mouse.y)/BOUNCER_SIZE + 1 - rotated)].disable();
					  }
				  }
			  }
			  else if (ev.mouse.button == 2)
				  rotated = 1 - rotated;
			  /*else if (ev.mouse.button == 3)
				  cout << "(" << blocks[(int)(ev.mouse.x)/BOUNCER_SIZE][(int)(ev.mouse.y)/BOUNCER_SIZE].getX() << "," << blocks[(int)(ev.mouse.x)/BOUNCER_SIZE][(int)(ev.mouse.y)/BOUNCER_SIZE].getY() << "): "
				  << blocks[(int)(ev.mouse.x)/BOUNCER_SIZE][(int)(ev.mouse.y)/BOUNCER_SIZE].dir() << endl;*/
		  }

		  if(redraw && al_is_event_queue_empty(event_queue)) {
			 redraw = false;
			 taken = GRID_W * GRID_H; // initialize as all taken

			 al_clear_to_color(al_map_rgb(0,0,0));
			 al_draw_text(font, al_map_rgb(font_r,font_g,font_b), SCREEN_W / 2.0, 0, ALLEGRO_ALIGN_CENTRE, "BoxPlex");

			 for(int i = 0; i < GRID_W + 2; i++)
				 for(int j = 0; j < GRID_H + 2; j++)
				 {
					 if(!line_test) // frames for the line
					 {
						 switch(blocks[i][j].dir())
						 {
						 case LEFT:
							 if(i > 0 && blocks[i - 1][j].able())
								 blocks[i - 1][j].go(LEFT);
							 else
								 destroying_ul = false;
							 if(!destroying_ul)
							 {
								 blocks[i][j].go(NONE);
								 blocks[i][j].disable();
							 }
							 break;
						 case RIGHT:
							 if(i < GRID_W + 1 && blocks[i + 1][j].able())
							 {
								 if(!line_started && !blocks[i + 1][j].dir())
								 {
									 blocks[i + 1][j].go(RIGHT);
						 			 line_started = true;
					 			 }
							 }
							 else if(line_started) // reached end of line
							 {
								line_started = false;
								destroying_dr = false;
							 }
							 if(i == GRID_W + 1 || !blocks[i + 1][j].able())
							 {
								 destroying_dr = false;
								 for(int k = 0; k < GRID_W + 2; k++)
									for(int j = 0; j < GRID_H + 2; j++)
										if(blocks[k][j].dir() == RIGHT)
										{
											blocks[k][j].disable();
											blocks[k][j].go(NONE);
										}
							 }
							 if(!destroying_dr)
							 {
								 blocks[i][j].go(NONE);
								 blocks[i][j].disable();
							 }
							 break;
						 case UP:
							 if(j > 0 && blocks[i][j - 1].able())
								 blocks[i][j - 1].go(UP);
							 else
								 destroying_ul = false;
							 if(!destroying_ul)
							 {
								 blocks[i][j].go(NONE);
								 blocks[i][j].disable();
							 }
							 break;
						 case DOWN:
							 if(j < GRID_H + 1 && blocks[i][j + 1].able())
							 {
								 if(!line_started && !blocks[i][j + 1].dir())
								 {
									blocks[i][j + 1].go(DOWN);
									line_started = true;
								 }
								 else
									 line_started = false;
							 }
							 else
								 destroying_dr = false;
							 if(j == GRID_H + 1 || !blocks[i][j + 1].able())
							 {
								 destroying_dr = false;
								 for(int k = 0; k < GRID_W + 2; k++)
									for(int j = 0; j < GRID_H + 2; j++)
										if(blocks[k][j].dir() == DOWN)
										{
											blocks[k][j].go(NONE);
											blocks[k][j].disable();
										}
							 }
							 if(!destroying_dr)
							 {
								 blocks[i][j].go(NONE);
								 blocks[i][j].disable();
							 }
							 break;
						 }
					 }
					 if(i > 0 && j > 0 && i < GRID_W  + 1 && j < GRID_H + 1)
					 {
						 if(blocks[i][j].able())
						 {
							 if(!blocks[i][j].dir())
								 blocks[i][j].drawBmp();
							 else
								 blocks[i][j].drawPend();
							 taken--;
						 }
					 }
				 } // end for
			 line_started = false;

			al_draw_textf(font, al_map_rgb(font_r,font_g,font_b), SCREEN_W / 2.0 + BOUNCER_SIZE, SCREEN_H - FONT_SIZE - 2.0, ALLEGRO_ALIGN_CENTRE, "Taken: %d%%", 100 * taken / grid_count);
			al_draw_textf(font, al_map_rgb(font_r,font_g,font_b), 0, 0, ALLEGRO_ALIGN_LEFT, "Level: %d", levels);
			al_draw_textf(font, al_map_rgb(font_r,font_g,font_b), SCREEN_W - 6*BOUNCER_SIZE, 0, ALLEGRO_ALIGN_LEFT, "Lives: %d", lives);
			al_draw_textf(font, al_map_rgb(font_r,font_g,font_b), 0, SCREEN_H - FONT_SIZE - 2.0, ALLEGRO_ALIGN_LEFT, "People saved: %d", score);
			al_draw_textf(font, al_map_rgb(font_r,font_g,font_b), SCREEN_W - 9*BOUNCER_SIZE, SCREEN_H - FONT_SIZE - 2.0, ALLEGRO_ALIGN_LEFT, "Record: %d", HIGH_SCORE);
			/*if(destroying_ul)
				al_draw_text(font,al_map_rgb(font_r,font_g,font_b),0,SCREEN_H - FONT_SIZE - 2.0, ALLEGRO_ALIGN_LEFT, "UL");
			if(destroying_dr)
				al_draw_text(font,al_map_rgb(font_r,font_g,font_b),40.0,SCREEN_H - FONT_SIZE - 2.0, ALLEGRO_ALIGN_LEFT, "DR");*/
			if(100 * taken / grid_count > 80)
				level_done = true;

			font_r = font_r % 255 + 20;
			font_g = font_g % 255 + 40;
			font_b = font_b % 255 + 60;
			 for(int i = 0; i < levels; i++)
				 boxes[i].drawBmp();

			 if(mouse_focus && !level_done)
				 al_draw_rotated_bitmap(mouse_bmp, al_get_bitmap_width(mouse_bmp)/2, al_get_bitmap_height(mouse_bmp)/2, mouse_x, mouse_y, ALLEGRO_PI / 2 * rotated, 0);

			 al_flip_display();
		  } // if redraw
	   } // while(!level_done)
	   // this part is after the level is complete
	   al_show_mouse_cursor(display);
	   if(lives && !doexit)
	   {
	       al_draw_text(font,al_map_rgb(0,255,0),SCREEN_W / 2.0,SCREEN_H / 2.0, ALLEGRO_ALIGN_CENTRE, "LEVEL SECURE");
	       score += (10 * taken / grid_count) + 5*lives + 10*levels;
	   }
	   else if(!doexit)
	   {
		   al_draw_text(font,al_map_rgb(255,0,0),SCREEN_W / 2.0,SCREEN_H / 2.0, ALLEGRO_ALIGN_CENTRE, "FAILURE");
		   levels = 0;
		   if(score > HIGH_SCORE)
               HIGH_SCORE = score;
            score = 0;
	   }
	   al_flip_display();
	   while(!level_start && !doexit)
	   {
		   ALLEGRO_EVENT ev;
		  al_wait_for_event(event_queue, &ev);

		  if(ev.type == ALLEGRO_EVENT_KEY_DOWN)
			  if(ev.keyboard.keycode == ALLEGRO_KEY_ESCAPE)
			  {
				  doexit = true;
				  level_start = true;
			  }
			if(ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE)
		  {
			  level_start = true;
			  doexit = true;
		  }

		  if(ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN)
			  if(ev.mouse.button == 1)
				  level_start = true;
	   } // while !level_start && !doexit
	   level_start = false;
	   al_hide_mouse_cursor(display);
	   level_done = false;
	   if(levels < MAX_LEVEL)
		   levels++;
	   lives++;
	   destroying_ul = destroying_dr = false;
		line_started = false;
	   for(int i = 0; i < GRID_W + 2; i++)
			for(int j = 0; j < GRID_H + 2; j++)
			{
				blocks[i][j].go(NONE);
				blocks[i][j].enable();
				blocks[i][j].uncheck();
			}
   } // while(!doexit)

   for(int i = 0; i < levels; i++)
		  boxes[i].destBmp();
   for(int i = 0; i < GRID_W + 2; i++)
		for(int j = 0; j < GRID_H + 2; j++)
			blocks[i][j].destBmp();
    config.close();
    config.open("config.cfg", fstream::in | fstream::out);
    for(int i = 1; i < 10; i++)
    {
        getline(config,str);
        pos += str.size() + 2;
    }
    str = "10 ";
    os << HIGH_SCORE;
    str.append(os.str());
    str.append("\n");
    config.seekp(pos,ios::beg);
    char *chrstr = new char[str.length() + 1];
    strcpy(chrstr, str.c_str());
    config.write(chrstr,str.size());
    al_destroy_timer(timer);
    al_destroy_display(display);
    al_destroy_event_queue(event_queue);
    al_destroy_font(font);
    al_destroy_font(banner_font);
    al_destroy_sample(attack);
    al_destroy_sample(start);
	al_destroy_sample(quit);
	al_destroy_sample(song);
	config.close();
   delete[] boxes;
   delete[] chrstr;
   for(int i = 0; i < GRID_W + 2; i++)
		delete[] blocks[i];
   delete[] blocks;

   return 0;
}
